package com.wuhao.code.check

import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiEnumConstant
import com.intellij.psi.PsiNamedElement
import com.wuhao.code.check.model.getAllSuperClasses
import com.wuhao.code.check.model.getAnnotationPropertyValue
import org.apache.ibatis.ognl.Ognl
import org.jetbrains.kotlin.nj2k.postProcessing.resolve
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.lazy.data.KtClassInfoUtil

/**
 *
 * @author 吴昊
 * @date 2024/1/24 9:51
 * @since v0.0.0
 * @version 1.0
 */


/**
 * @param annoClass
 * @param propertyName
 * @return
 */
fun KtAnnotated.getKtElStringListAnnotationValue(
    annoClass: String,
    propertyName: String
): List<String> {
  val anno = this.getAnnotation(annoClass)
  anno?.valueArguments?.forEach {
    val n = it.getArgumentName()
    if ((propertyName == "value" && !it.isNamed())
        || (n is KtValueArgumentName && n.text == propertyName)
    ) {
      val exp = it.getArgumentExpression()
      if (exp is KtCollectionLiteralExpression) {
        return exp.innerExpressions.filterIsInstance<KtStringTemplateExpression>()
            .map { expression ->
              expression.text.drop(1).dropLast(1)
            }
      }
    }
  }
  return listOf()
}

fun PsiElement.getName(): String? {
  if (this is PsiNamedElement) {
    return this.name
  }
  return null
}

fun PsiElement.getAllFields(): List<PsiElement> {
  return this.getAllSuperClasses().map {
    when (it) {
      is KtClass  -> {
        val info = KtClassInfoUtil.createClassOrObjectInfo(it)
        info.declarations.filterIsInstance<KtProperty>() + info.primaryConstructorParameters.filter { parameter ->
          parameter.hasValOrVar()
        }
      }

      is PsiClass -> {
        it.allFields.toList()
      }

      else        -> {
        listOf()
      }
    }
  }.flatten()
}

/**
 * @param annoClass
 * @param propertyName
 * @return
 */
fun getStringAnnoValue(el: KtAnnotated, annoClass: String, propertyName: String): String? {
  val valueExpression = el.getAnnotationPropertyValue(annoClass, propertyName) ?: return null
  return valueExpression.toStringValue()
}

fun KtExpression.toStringValue(): String? {
  return when (this) {
    is KtStringTemplateExpression -> {
      this.text.drop(1).dropLast(1)
    }

    is KtDotQualifiedExpression   -> {
      val target = (this.selectorExpression as KtNameReferenceExpression).resolve()
      getExpressionTargetValue(target, this)
    }

    is KtNameReferenceExpression  -> {
      this.text
    }

    is KtBinaryExpression         -> {
      parseConstantValue(this)
    }

    else                          -> null
  }
}

fun getExpressionTargetValue(target: PsiElement?, it: PsiElement): String {
  return when (target) {
    is KtProperty               -> {
      val init = target.initializer
      if (init is KtBinaryExpression) {
        parseConstantValue(init)
      } else {
        init?.text ?: ""
      }
    }

    is KtEnumEntry              -> target.name ?: error("无法解析${it.text}的值")
    is KtDotQualifiedExpression -> target.toStringValue() ?: error("无法解析${it.text}的值")
    is PsiEnumConstant          -> target.name
    else                        -> error("无法解析${it.text}的值")
  }
}


/**
 * @param expression
 * @return
 */
fun parseConstantValue(expression: KtBinaryExpression): String {

  val exp = expression.children.joinToString("") {
    when (it) {
      is KtDotQualifiedExpression  -> {
        val target = (it.selectorExpression as KtNameReferenceExpression).resolve()
        getExpressionTargetValue(target, it)
      }

      is KtBinaryExpression        -> {
        parseConstantValue(it)
      }

      is KtNameReferenceExpression -> {
        val target = it.resolve()
        getExpressionTargetValue(target, it)
      }

      else                         -> {
        it.text
      }
    }
  }
  return Ognl.getValue(exp, Object(), String::class.java).toString()
}


